package com.mask.share.life.jk;

import com.google.common.collect.Lists;
import com.mask.share.life.jk.hander.ImageHandler;
import com.mask.share.life.jk.hander.handler_result.ImageHandlerResult;
import com.mask.share.utils.ImageOpenCVUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.queue.CircularFifoQueue;
import org.opencv.core.Mat;
import org.opencv.videoio.VideoCapture;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 单独线程执行把视频转为帧，并且截取指定区域
 */
@Slf4j
public class VideoWorker {
    private static ExecutorService executorService;

    static {
        ImageOpenCVUtils.loadLib();
    }

    /**
     * 源数据文件
     */
    private List<String> sourceVideos;
    /**
     * 目标像素区域
     */
    private int x, y, x1, y1 = 0;
    private int bufferSize = 50;
    private CircularFifoQueue<Mat> origImageBuffer = new CircularFifoQueue<>(bufferSize + 1);
    private CircularFifoQueue<Mat> dealImageBuffer = new CircularFifoQueue<>(bufferSize + 1);
    private List<ImageHandler> handlers;
    private ImageHandlerResult imageHandlerResult;


    private VideoWorker() {

    }

    public void doWork() {
        /* 一边读取视频，一边进行处理*/
        for (String sourceVideo : sourceVideos) {
            VideoCapture videoCapture = new VideoCapture(sourceVideo);
            if (!videoCapture.isOpened()) {
                log.error("无法打开视频文件:{}", sourceVideo);
                continue;
            }
            log.info("开始读取视频文件:{}", sourceVideo);


            // 读一帧，处理一帧
            Mat frame = new Mat();
            while (videoCapture.read(frame)) {
                // 把截图放入buffer中
                if (x == 0 && y == 0 && x1 == 0 && y1 == 0) {
                    // 无需截图
                    Mat clone = frame.clone();
                    origImageBuffer.add(clone);
                    dealImageBuffer.add(clone);
                } else {
                    Mat clone = frame.clone();
                    Mat catMat = ImageOpenCVUtils.截取图片(frame, x, y, x1, y1);
                    origImageBuffer.add(clone);
                    dealImageBuffer.add(catMat);
                }

                // 处理，有几种处理方式就用多少个线程，并且等待处理完成
                List<Future<ExtInfo>> resultTemp = Lists.newArrayList();
                for (ImageHandler handler : handlers) {
                    Future<ExtInfo> submit = executorService.submit(() -> handler.handle(origImageBuffer, dealImageBuffer));
                    resultTemp.add(submit);
                }

                List<ExtInfo> list = resultTemp
                        .stream()
                        .map(extInfoFuture -> {
                                    try {
                                        return extInfoFuture.get();
                                    } catch (InterruptedException e) {
                                    } catch (ExecutionException e) {
                                    }
                                    return null;
                                }
                        )
                        .toList();
                imageHandlerResult.handle(list);
            }
            // 当前文件读完，关闭相关信息
            videoCapture.release();
        }

        executorService.shutdown();

    }

    public static class Builder {
        VideoWorker videoWorker = new VideoWorker();
        private int threadCore = 5;

        public Builder(List<String> sourceVideos,
                       List<ImageHandler> handlers,
                       ImageHandlerResult imageHandlerResult) {
            videoWorker.sourceVideos = sourceVideos;
            videoWorker.handlers = handlers;
            videoWorker.imageHandlerResult = imageHandlerResult;
        }

        public Builder needCatImage(int x, int y, int x1, int y1) {
            videoWorker.x = x;
            videoWorker.y = y;
            videoWorker.x1 = x1;
            videoWorker.y1 = y1;
            return this;
        }

        public Builder bufferSize(int bufferSize) {
            videoWorker.bufferSize = bufferSize;
            return this;
        }

        public Builder setThreadCore(int threadCore) {
            this.threadCore = threadCore;
            return this;
        }

        public VideoWorker build() {
            executorService = Executors.newFixedThreadPool(threadCore);
            return videoWorker;
        }
    }
}
